#include "MacArrayMessage.hpp"

using namespace BFT::messages;

MacArrayMessage::MacArrayMessage()
{
	//dummy constructor, invoked only by some constructors of NextBatch before invoking the real one
}

MacArrayMessage::MacArrayMessage(uint16_t _tag, uint32_t _size, uint32_t _sender, uint32_t _arraySize) 
	: VerifiedMessageBase(_tag, _size, computeAuthenticationSize(_arraySize))
{
	/*
	fprintf(stderr,"MacArrayMessage constructor\n");
	*/
	info.sender = _sender;
	info.digest_start = getAuthenticationStartIndex() + SENDER_SIZE;
	info.macarray_size = _arraySize;
	info.macarray_start = info.digest_start + DIGEST_SIZE;
	info.digest.reset(new Digest());
	*((int32_t*)((char*)buffer.get()+ getAuthenticationStartIndex()))= htonl(info.sender); // setting the sender
        //fprintf(stdout,"sender set\n");
        setMacBytes();
        /*
        printInfo();
        */
}

MacArrayMessage::MacArrayMessage(boost::shared_ptr<char> _buffer, int _buffer_length)
	: VerifiedMessageBase(_buffer, _buffer_length)
{
//	fprintf(stdout,"mam constructor has been called. buffer_length:%d\n",_buffer_length);
	//fprintf(stdout,"MAM constructor: authentication start index: %d\n",getAuthenticationStartIndex());
	info.sender = ntohl(*((uint32_t*)(buffer.get() + getAuthenticationStartIndex())));
	info.digest_start = getAuthenticationStartIndex() + SENDER_SIZE;
	
	boost::shared_ptr<char> tmp_bytes((char*)malloc(DIGEST_SIZE), free);
	memcpy( (void*)tmp_bytes.get(), (const void*)(buffer.get()+info.digest_start), sizeof(char)*DIGEST_SIZE ); 	
	info.digest= Digest::fromBytes( tmp_bytes, DIGEST_SIZE );//setting the digest
	tmp_bytes.reset();


	info.macarray_start = info.digest_start + DIGEST_SIZE;
	info.macarray_size = (getAuthenticationSize() - DIGEST_SIZE - SENDER_SIZE)/MAC_SIZE ;
	if( (getAuthenticationSize() - DIGEST_SIZE - SENDER_SIZE)%MAC_SIZE != 0) // check that the size of macarray (in bytes) is a multiple of the size of a mac
	{
		fprintf(stdout,"MAM constructor: authentication start index: %d\n",getAuthenticationStartIndex());
		fprintf(stderr,"something wrong in the creation of a MacArrayMessage\n");
		printInfo();
		exit(9);
	}
}

MacArrayMessage::MacArrayMessage(VerifiedMessageBase vmb)
	: VerifiedMessageBase::VerifiedMessageBase(vmb)
{
/*
	buffer=vmb.getBytes(buffer_length);
	VerifiedMessageBase::info.tag = vmb.getTag();
	VerifiedMessageBase::info.size = vmb.getTotalSize();
	VerifiedMessageBase::info.payload_start = vmb.getOffset();
	VerifiedMessageBase::info.payload_size = vmb.getPayloadSize();
	VerifiedMessageBase::info.authentication_size = vmb.getAuthenticationSize();
	VerifiedMessageBase::info.authentication_start = vmb.getAuthenticationStartIndex();
*/	
	//fprintf(stdout,"MAM constructor: authentication start index: %d\n",getAuthenticationStartIndex());
	info.sender = ntohl(*((uint32_t*)(buffer.get() + getAuthenticationStartIndex())));
	info.digest_start = getAuthenticationStartIndex() + SENDER_SIZE;
	
	boost::shared_ptr<char> tmp_bytes((char*)malloc(DIGEST_SIZE), free);
	memcpy( (void*)tmp_bytes.get(), (const void*)(buffer.get()+info.digest_start), sizeof(char)*DIGEST_SIZE ); 	
	info.digest= Digest::fromBytes( tmp_bytes, DIGEST_SIZE );//setting the digest
	tmp_bytes.reset();


	info.macarray_start = info.digest_start + DIGEST_SIZE;
	info.macarray_size = (getAuthenticationSize() - DIGEST_SIZE - SENDER_SIZE)/MAC_SIZE ;
	if( (getAuthenticationSize() - DIGEST_SIZE - SENDER_SIZE)%MAC_SIZE != 0) // check that the size of macarray (in bytes) is a multiple of the size of a mac
	{
		fprintf(stdout,"MAM constructor: authentication start index: %d\n",getAuthenticationStartIndex());
		fprintf(stderr,"something wrong in the creation of a MacArrayMessage from a VMB\n");
		printInfo();
		exit(9);
	}


}

void MacArrayMessage::printInfo() 
{
	VerifiedMessageBase::printInfo();
	fprintf(stdout,"MacArrayMessage information:\n");
	fprintf(stdout,"sender ID........................:%d\n",info.sender);
	fprintf(stdout,"digest start index...............:%d\n",info.digest_start);
	fprintf(stdout,"digest...........................:");
	for(int i=0; i < DIGEST_SIZE; i++)
	{
		fprintf(stdout,"|%02X",(unsigned char)info.digest.get()->getBytes().get()[i]);
	}
	fprintf(stdout,"\n");
	fprintf(stdout,"mac array start index............:%d\n",info.macarray_start);
	fprintf(stdout,"number of macs...................:%d\n",info.macarray_size);
}

boost::shared_ptr<char> MacArrayMessage::getMacArray(int& _macs_number)
{
	boost::shared_ptr<char> mac_array ((char*)malloc( sizeof(char)*info.macarray_size*MAC_SIZE), free);
	memcpy((void*)mac_array.get(),(const void*)(buffer.get()+info.macarray_start),sizeof(char)*info.macarray_size*MAC_SIZE);
	_macs_number=info.macarray_size;
	return mac_array;
}

boost::shared_ptr<Digest> MacArrayMessage::getAuthenticationDigest()
{
	
	if (info.digest.use_count() == 0)
	{
		// create the new digest
		if (INSECURE)
		{
			info.digest.reset(new Digest());
		}
		else
		{
			int temp_size;
			info.digest.reset(new Digest(getBytes(temp_size), 0, getTotalSize() - getAuthenticationSize()));
		}
	
		//add digest to the bytearray
		memcpy((void*)(buffer.get()[getDigestStartIndex()]),(const void*)(info.digest.get()->getBytes().get()),DIGEST_SIZE);
	}
	return info.digest;	
}

bool MacArrayMessage::checkAuthenticationDigest()
{
	if(info.digest.use_count()==0)
	{
		fprintf(stderr,"authentication usecount be non 0 to be checked\n");
		exit(14);
	}
	
	//TODO do proper validation here!
	/* 
	boost::shared_ptr<Digest> tmp_digest;
	tmp_digest = info.digest;
	info.digest.reset();
	return tmp_digest.get()->equals(getAuthenticationDigest());
	*/
	return true;
}

bool MacArrayMessage::isValid()
{
	return checkAuthenticationDigest();
}

void MacArrayMessage::setMacBytes()
{
	memset( (void*)(buffer.get()+getMacarrayStartIndex()), 255, MAC_SIZE);
	buffer.get()[getMacarrayStartIndex()]=(uint8_t)65;
	buffer.get()[getMacarrayStartIndex()+MAC_SIZE-1]=(uint8_t)65;
}
